#include "cem/json_config_builder.h"
#include "cem/config_directory.h"

#include <cjson/cJSON.h>

#include <memory>
#include <string>
#include <vector>

namespace cem
{

// 将 cJSON 节点构造成 ConfigValue（递归）
static std::shared_ptr<ConfigValue> build_value_from_cjson(const cJSON * node, DiagnosticList * diags);

// 将 cJSON 数组构造成 ConfigArray
static std::shared_ptr<ConfigArray> build_array_from_cjson(const cJSON * node, DiagnosticList * diags)
{
    auto arr = std::make_shared<ConfigArray>();
    arr->items.reserve(static_cast<size_t>(cJSON_GetArraySize(node)));
    for (const cJSON * child = node->child; child != nullptr; child = child->next)
    {
        arr->items.push_back(build_value_from_cjson(child, diags));
    }
    return arr;
}

// 将 cJSON 对象构造成 ConfigObject
static std::shared_ptr<ConfigObject> build_object_from_cjson(const cJSON * node, DiagnosticList * diags)
{
    auto obj = std::make_shared<ConfigObject>();
    for (const cJSON * child = node->child; child != nullptr; child = child->next)
    {
        std::string key = child->string ? child->string : "";
        obj->props.emplace(std::move(key), build_value_from_cjson(child, diags));
    }
    return obj;
}

static std::shared_ptr<ConfigValue> build_value_from_cjson(const cJSON * node, DiagnosticList * diags)
{
    (void)diags; // 目前构建阶段不生成错误，仅在类型不支持时使用空
    auto v = std::make_shared<ConfigValue>();
    if (cJSON_IsNull(node))
    {
        v->value = nullptr;
    }
    else if (cJSON_IsBool(node))
    {
        v->value = (node->type & cJSON_False) ? false : true;
    }
    else if (cJSON_IsNumber(node))
    {
        // cJSON 没有 64 位整判断，这里尽量保留整数性
        double as_double = node->valuedouble;
        long long as_ll = static_cast<long long>(node->valuedouble);
        if (static_cast<double>(as_ll) == as_double)
            v->value = static_cast<int64_t>(as_ll);
        else
            v->value = as_double;
    }
    else if (cJSON_IsString(node))
    {
        v->value = std::string(node->valuestring ? node->valuestring : "");
    }
    else if (cJSON_IsArray(node))
    {
        v->value = build_array_from_cjson(node, diags);
    }
    else if (cJSON_IsObject(node))
    {
        v->value = build_object_from_cjson(node, diags);
    }
    else
    {
        // 不支持的类型，置空
        v->value = nullptr;
    }
    return v;
}

/*
 * 从 cJSON 根节点构建一个只读配置目录。
 * 输入必须是一个 JSON 对象；若不是对象则返回空目录并记录错误。
 */
ConfigDirectory build_config_directory_from_cjson(const cJSON * root, DiagnosticList * diags)
{
    if (root == nullptr)
    {
        if (diags)
            diags->push_back({DiagnosticSeverity::Error, "null json root", {"$"}});
        return ConfigDirectory{"$", ConfigObject{}, {"$"}};
    }
    if (!cJSON_IsObject(root))
    {
        if (diags)
            diags->push_back({DiagnosticSeverity::Error, "json root must be an object", {"$"}});
        return ConfigDirectory{"$", ConfigObject{}, {"$"}};
    }

    auto obj = build_object_from_cjson(root, diags);
    return ConfigDirectory{"$", *obj, {"$"}};
}

/*
 * 从原始 JSON 文本构建只读配置目录。
 * 当解析失败时，返回空目录并附带一条诊断错误（若 diags 非空）。
 */
ConfigDirectory build_config_directory_from_json_text(const std::string & json_text, DiagnosticList * diags)
{
    cJSON * root = cJSON_Parse(json_text.c_str());
    if (root == nullptr)
    {
        if (diags)
            diags->push_back({DiagnosticSeverity::Error, "failed to parse json text", {"$"}});
        return ConfigDirectory{"$", ConfigObject{}, {"$"}};
    }
    // 确保释放
    struct FreeGuard
    {
        cJSON * p;
        ~FreeGuard() { cJSON_Delete(p); }
    } guard{root};

    return build_config_directory_from_cjson(root, diags);
}

} // namespace cem